package wsi.match;

import java.util.List;

import org.rygh.core.html.element.HtmlElement;
import org.rygh.core.html.element.HtmlUl;
import org.rygh.core.xml.XmlLeaf;
import org.rygh.core.xml.XmlObject;
import org.rygh.semantic.model.ex.PredicatePath;
import org.rygh.semantic.model.rdf.Predicate;

import wsi.exp.ExpXml.Tag;
import wsi.script.AppConfig;
import wsi.subgraph.tree.EntityLeaf;

public class MatchProperty {

	public enum MatchType {
		EXISTS, FORALL
	}

	public interface Threshold {
		double MINORITY = 0.2;
		double MIDDLE = 0.5;
		double MAJORITY = 0.8;
		double PERFECT_MATCH = AppConfig.shared().getResolutionThreshold();
	}

	public static boolean equalsToAny(List<MatchProperty> list1,
			List<MatchProperty> list2) {
		for (MatchProperty p1 : list1)
			if (p1.equalsToAny(list2))
				return true;

		return false;
	}

	public static MatchProperty exists() {
		return new MatchProperty(MatchType.EXISTS, 1);
	}

	public static MatchProperty forall(String path, String database) {
		MatchProperty mp = new MatchProperty(MatchType.FORALL, path);
		mp.setDatabase(database);
		return mp;
	}

	public static MatchProperty getMatchProperty(XmlLeaf node) {
		String db = AppConfig.shared().getKnowledgeDatabase();
		MatchProperty property = null;

		for (XmlLeaf child : node.getChildren()) {
			XmlObject xmlObject = child.getValue();

			property = getMatchProperty(xmlObject);
			String type = xmlObject.getAttribute(Tag.type);

			if (type.equals(MatchType.EXISTS.name()))
				break;
			else if (type.equals(MatchType.FORALL.name())) {
				String db2 = xmlObject.getAttribute(Tag.database);

				if (db.equals(db2))
					break;
			}
		}

		return property;
	}

	public static MatchProperty getMatchProperty(XmlObject xmlObject) {
		MatchProperty property = null;

		String type = xmlObject.getAttribute(Tag.type);

		if (type.equals(MatchType.EXISTS.name())) {
			property = new MatchProperty(MatchType.EXISTS);
			property.setSubtreeIndex(Integer.parseInt(xmlObject.getValue()));
		} else if (type.equals(MatchType.FORALL.name())) {
			property = new MatchProperty(MatchType.FORALL);
			property.setPredicatePath(new PredicatePath(xmlObject.getValue()));
		}

		assert property != null;
		return property;
	}

	private final MatchType type;

	private String database;
	private int subtreeIndex;
	private PredicatePath predicatePath;

	public MatchProperty(MatchType type) {
		this.type = type;
	}

	public MatchProperty(MatchType type, int subtreeIndex) {
		this.type = type;
		this.subtreeIndex = subtreeIndex;
	}

	public MatchProperty(MatchType type, String path) {
		this.type = type;
		this.predicatePath = new PredicatePath(path);
	}

	/**
	 * FORALL@INPUT = EXISTS@1, not implemented.
	 * 
	 * @param o
	 * @return
	 */
	public boolean equalsTo(MatchProperty o) {
		if (!type.equals(o.getType()))
			return false;

		if (type == MatchType.EXISTS)
			return subtreeIndex == o.subtreeIndex;
		else
			return predicatePath.getKey().equals(o.predicatePath.getKey());
	}

	public boolean equalsToAny(List<MatchProperty> properties) {
		for (MatchProperty i : properties)
			if (equalsTo(i))
				return true;

		return false;
	}

	public PredicatePath getPredicatePath() {
		return predicatePath;
	}

	public int getSubtreeIndex() {
		return subtreeIndex;
	}

	public MatchType getType() {
		return type;
	}

	public XmlObject getXmlObject() {
		XmlObject obj = new XmlObject(Tag.match.name());
		obj.addAttribute(Tag.type.name(), type.name());

		if (database != null)
			obj.addAttribute(Tag.database.name(), database);

		switch (type) {
		case EXISTS:
			obj.setValue(Integer.toString(subtreeIndex));
			break;

		case FORALL:
			obj.setValue(predicatePath.getKey());
			break;
		}

		return obj;
	}

	public boolean isExistsMatch() {
		return type == MatchType.EXISTS;
	}

	public void setDatabase(String database) {
		this.database = database;
	}

	public void setPredicatePath(PredicatePath predicatePath) {
		this.predicatePath = predicatePath;
	}

	public void setSubtreeIndex(int subtreeIndex) {
		assert subtreeIndex > 0;
		this.subtreeIndex = subtreeIndex;
	}

	public HtmlElement toHtmlElement() {
		HtmlUl ul = new HtmlUl();

		ul.addElement(String.format("Match Property: %s", type.name()));

		switch (type) {
		case EXISTS:
			ul.addElement(String.format("Best index: %d", subtreeIndex));
			break;

		case FORALL:
			ul.addElement(String.format("Entity predicate path: %s",
					predicatePath.getKey()));
			break;
		}

		return ul;
	}

	@Override
	public String toString() {
		switch (type) {
		case EXISTS:
			return String.format("Type: %s, Index: %d.", type.name(),
					subtreeIndex);

		case FORALL:
			return String.format("Type: %s, Predicate Path: %s.", type.name(),
					predicatePath.getKey());

		default:
			return null;
		}
	}

	public boolean validateMatchedPath(EntityLeaf node) {
		if (predicatePath.isEmpty()) {
			// System.out.printf("validate %s %b %s\n",
			// node.getValue().getValue(), node.isRoot(), node.getValue()
			// .getPredicate());

			if (!node.isRoot())
				return false;
		} else {
			Predicate pred = node.getValue().getPredicate();

			if (!predicatePath.getLastPredicate().equals(pred))
				return false;
		}

		return true;
	}
}
